from utils.e_mail import sendMail
from utils.log_util import saveLog
from utils.time_util import current_time, gmt_time
import MetaTrader5 as mt5
from enum import Enum
from constant.globl_constant import fibList, strategyList
import constant.param_config as config


class Order(Enum):
    CREATE_BUY = 1
    CREATE_SELL = 2
    CLOSE_BUY = 3
    CLOSE_SELL = 4
    CLOSE_ALL = 5


class OrderType(Enum):
    BUY = 1
    SELL = 2
    ALL = 3


class OrderControl:
    """
    订单管理中心
    """

    listener = None
    lastNotice = 0
    timeSpace = 300  # 300秒

    def closeOrder(self, orderInfo, comment=""):
        """
        关闭订单
        """
        close_request = None

        if comment != "":
            comment = "美金收割机_订单关闭_" + comment
        else:
            comment = "美金收割机_订单关闭"

        if orderInfo.type == mt5.ORDER_TYPE_BUY_LIMIT or orderInfo.type == mt5.ORDER_TYPE_SELL_LIMIT:
            close_request = {
                "action": mt5.TRADE_ACTION_REMOVE,
                "order": orderInfo.ticket,
            }
        elif orderInfo.type == mt5.POSITION_TYPE_BUY:  # 多单
            trade_type = mt5.ORDER_TYPE_SELL
            price = self.getCurrentPrice(orderInfo.symbol,
                                         orderType=Order.CREATE_BUY)
        elif orderInfo.type == mt5.POSITION_TYPE_SELL:  # 空单
            trade_type = mt5.ORDER_TYPE_BUY
            price = self.getCurrentPrice(orderInfo.symbol,
                                         orderType=Order.CREATE_SELL)

        if orderInfo.type == mt5.POSITION_TYPE_BUY or orderInfo.type == mt5.POSITION_TYPE_SELL:
            close_request = {
                "action": mt5.TRADE_ACTION_DEAL,
                "symbol": orderInfo.symbol,
                "volume": orderInfo.volume,
                "type": trade_type,
                "position": orderInfo.ticket,
                "price": price,
                # "deviation": 1000,
                "magic": orderInfo.magic,
                "comment": comment,
            }
        return self.sendRequest(close_request)

    def createOrder(self,
                    t,
                    symbol,
                    limitPrice=0,
                    priceSp=0,
                    priceTp=0,
                    lots=0,
                    remark=""):
        """
        创建订单
        limitPrice  挂单价格
        limitSp     挂单止损价
        limitTp     挂单止盈价
        """

        sp = 0
        tp = 0
        defaultLots = 0

        printStr = "创建订单"
        comment = "美金收割机"
        if remark != "":
            comment = comment + "_" + remark

        action = mt5.TRADE_ACTION_DEAL
        point = mt5.symbol_info(symbol).point

        tagerloss = config.getTagerloss()
        if lots == 0:
            defaultLots = config.getDefaultLots()
        else:
            defaultLots = lots

        if t == mt5.ORDER_TYPE_SELL_LIMIT or t == mt5.ORDER_TYPE_BUY_LIMIT:
            trade_type = t
            price = self.toFloat(limitPrice)
            tp = self.toFloat(priceTp)
            sp = self.toFloat(priceSp)
            action = mt5.TRADE_ACTION_PENDING
            printStr = "创建挂单"
        elif t == OrderType.BUY:
            trade_type = mt5.ORDER_TYPE_BUY
            price = mt5.symbol_info_tick(symbol).ask
            if priceTp == 0:
                tp = self.getTakePricePrice(price, t)
            else:
                tp = self.toFloat(priceTp)

            if priceSp is not None:
                if priceSp == 0:
                    if tagerloss != 0:
                        sp = price - tagerloss
                else:
                    sp = self.toFloat(priceSp)
        elif t == OrderType.SELL:
            trade_type = mt5.ORDER_TYPE_SELL
            price = mt5.symbol_info_tick(symbol).bid
            if priceTp == 0:
                tp = self.getTakePricePrice(price, t)
            else:
                tp = self.toFloat(priceTp)

            if priceSp is not None:
                if priceSp == 0:
                    if tagerloss != 0:
                        sp = price + tagerloss
                else:
                    sp = self.toFloat(priceSp)

        request = {
            "action": action,
            "symbol": symbol,
            "volume": float(defaultLots),
            "type": trade_type,
            "price": float(price),
            # "deviation": deviation,
            # "magic": ea_magic_number,
            "comment": comment,
        }

        # 自动止盈时设定止盈价
        if sp != 0:
            request["sl"] = float(sp)
        if tp != 0:
            request["tp"] = float(tp)

        # 挂单交易仅在当前交易日有效
        if t == mt5.ORDER_TYPE_SELL_LIMIT or t == mt5.ORDER_TYPE_BUY_LIMIT:
            request["type_time"] = mt5.ORDER_TIME_DAY

        sendResult = self.sendRequest(request)
        if sendResult != None:
            saveLog(printStr + "_" + remark)
            sendMail(printStr + "_" + remark, timer=False)
        else:
            sendMail("下单失败",
                     msg=printStr + "_" + remark + "请求体：" + str(request),
                     timer=False,
                     send=True)
        return sendResult

    def toFloat(self, value):
        try:
            return float(value)
        except ValueError:
            return value

    def changeOrder(self, trade, sl, tp, price=None, t=1):
        """
        修改指定持仓
        t: 1  实时单    2: 挂单
        """
        request = {
            "symbol": trade.symbol,
            "magic": trade.magic,
        }

        if t == 1:
            request["action"] = mt5.TRADE_ACTION_SLTP
            request["position"] = trade.ticket
        else:
            request["action"] = mt5.TRADE_ACTION_MODIFY
            request["price"] = self.toFloat(price)
            request["order"] = trade.ticket

        if sl != 0:
            request["sl"] = self.toFloat(sl)
        if tp != 0:
            request["tp"] = self.toFloat(tp)
        return self.sendRequest(request)

    def sendRequest(self, request):
        """
        从程序端向交易服务器发送请求来执行交易操作
        """
        saveLog("下单请求：" + str(request))
        result = mt5.order_send(request)
        saveLog("下单结果：" + str(result))
        if result is not None and result.retcode != mt5.TRADE_RETCODE_DONE:  # 下单成功
            comment = result.comment
            positionId = result[10].position
            errotMsg = "订单 " + str(positionId) + " 操作失败：" + comment
            print(errotMsg)
            saveLog(errotMsg)
        return result

    def getTakePricePrice(self, currentPrice, t):
        """
        获取止盈价位
        """

        goldPriceOffset = config.getPriceOffset()
        if t == OrderType.BUY:
            if len(strategyList) != 0 and "buy" in strategyList[0]:
                strategyBuy = strategyList[0]["buy"]
                if len(strategyBuy) != 0 and "tp" in strategyBuy:
                    buyTpPriceArr = strategyBuy["tp"]
                    for index in range(0, len(buyTpPriceArr)):
                        if currentPrice < buyTpPriceArr[
                                index] - goldPriceOffset:
                            return buyTpPriceArr[index]
            return currentPrice + 2
        elif t == OrderType.SELL:
            if len(strategyList) != 0 and "sell" in strategyList[0]:
                strategySell = strategyList[0]["sell"]
                if len(strategySell) != 0 and "tp" in strategySell:
                    sellTpPriceArr = strategySell["tp"]
                    for index in range(len(sellTpPriceArr) - 1, -1, -1):
                        if currentPrice > sellTpPriceArr[
                                index] + goldPriceOffset:
                            return sellTpPriceArr[index]
            return currentPrice - 2
        return 0

    def getCurrentPrice(self, symbol, orderType=Order.CREATE_BUY):
        """
        获取当前价格
        """
        if orderType == Order.CREATE_BUY:
            return mt5.symbol_info_tick(symbol).bid
        elif orderType == Order.CREATE_SELL:
            return mt5.symbol_info_tick(symbol).ask

    def nineKLine(self, symbol):
        """
        神奇九转
        """
        result = self.listener.nineKLine(symbol)
        if len(result) != 0:
            if self.lastNotice == 0 or current_time(
            ) - self.lastNotice > self.timeSpace:
                timeFrameArr = ""
                timeStr = ""
                for mapIndex in range(0, len(result)):
                    mapKey = list(result[mapIndex].keys())[0]
                    timeFrameArr = timeFrameArr + mapKey
                    if mapIndex == 0:
                        timeStr = list(result[mapIndex].values())[0]
                    if mapIndex != len(result) - 1:
                        timeFrameArr = timeFrameArr + "、"
                alert = self.symble + "_" + timeFrameArr + "周期出现转折信号_" + "GMT：" + timeStr
                saveLog(alert)
                sendMail(alert)
                self.lastNotice = current_time()

    def nineKLine(self):
        """
        神奇九转检测
        """

        timeframe = []
        kLineList = self.getKLine(self.symble, 15)
        for keys, values in kLineList.items():
            k1 = values[0]
            k2 = values[1]
            k3 = values[2]
            k4 = values[3]

            k1High = k1[2]
            k1Low = k1[3]
            k2High = k2[2]
            k2Low = k2[3]
            k3High = k3[2]
            k3Low = k3[3]
            k4High = k4[2]
            k4Low = k4[3]

            # 1:下跌    2:上涨
            kState = 0
            if k1High > k3High and k1High > k4High and k2High > k3High and k2High > k4High:
                kState = 1
            elif k1Low < k3Low and k1Low < k4Low and k2Low < k3Low and k2Low < k4Low:
                kState = 2

            if kState == 0:
                continue

            continuity = 0
            loopSize = 9
            for index in range(0, loopSize):
                childPrice = values[index]
                closePrice = childPrice[4]
                nextPrice = values[index + 4]
                nextClose = nextPrice[4]

                if kState == 1 and closePrice > nextClose:
                    if continuity != 2:
                        continuity = 1
                        if index == loopSize - 1:
                            timeframe.append({keys: gmt_time()})
                    elif continuity == 2:
                        break
                elif kState == 2 and closePrice < nextClose:
                    if continuity != 1:
                        continuity = 2
                        if index == loopSize - 1:
                            timeframe.append({keys: gmt_time()})
                    elif continuity == 1:
                        break
                else:
                    break
        return timeframe
